Our User Interface

This lesson shows the implementation of the application by designing a server and making a front end to see how things work.

Designing a server#

We haven’t yet coded the function with which our program must be started. This is (always) the function main() as in C, C++ or Java. In it, we will start our web server, e.g., we can start a local web server on port 8080 with the command:

http.ListenAndServe(":8080", nil)

The web server listens for incoming requests in an infinite loop, but we must also define how this server responds to these requests. We do this by making so-called HTTP handlers with the function HandleFunc. For example, by coding http.HandleFunc("/add", Add) we say that every request which ends in /add will call a function Add (still to be made).

Our program will have two HTTP handlers:

  • Redirect, which redirects short URL requests
  • Add, which handles the submission of new URLs

Schematically:

Handler Functions in goto
Handler Functions in goto

Our minimal main() could look like:

func main() {
  http.HandleFunc("/", Redirect)
  http.HandleFunc("/add", Add)
  http.ListenAndServe(":8080", nil)
}

Requests to /add will be served by the Add handler, where all the other requests will be served by the Redirect handler. Handler functions get information from an incoming request (a variable r of type *http.Request), and they make and write their response to a variable w of type http.ResponseWriter.

The Add function#

What must our Add function do?

  • Read in the long URL, which means reading the URL from an HTML-form contained in an HTTP request with r.FormValue("url").
  • Put it in the store using our Put method on the store.
  • Send the corresponding short URL to the user.

Each requirement translates in one code line:

func Add(w http.ResponseWriter, r *http.Request) {
  url := r.FormValue("url")
  key := store.Put(url)

  fmt.Fprintf(w, "%s", key)
}

The function Fprintf of the fmt package is used here to substitute a key in the string containing %s and then sends that string as a response back to the client. Notice that Fprintf writes to a ResponseWriter. In fact, Fprintf can write to any data structure that implements io.Writer(), which means that it implements a Write() method. io.Writer() is what is called, in Go, an interface. We see that through the use of interfaces, Fprintf is very general; it can write to a lot of different things. The use of interfaces is pervasive in Go and makes code more generally applicable. However, we still need a form; we can display a form by using Fprintf again. This time writing a constant to w. Let’s modify Add to display an HTML form when no URL is supplied:

func Add(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Content-Type", "text/html")
  url := r.FormValue("url")
  if url == "" {
    fmt.Fprint(w, AddForm)
    return
  }
  key := store.Put(url)

  fmt.Fprintf(w, "%s", key)
}
const AddForm = `
<html><body>
<form method="POST" action="/add">
URL: <input type="text" name="url">
<input type="submit" value="Add">
</form>
<\html><\body>`

In that case, we send the constant string AddForm to the client, which is the html, necessary for creating a form with an input field URL, and a submit button, which when pushed will post a request ending in /add. So, the Add handler function is again invoked, now with a value for URL from the text field. (The `` are needed to make a raw string, otherwise, strings are enclosed in “” as usual).

The Redirect function#

The Redirect function finds the key in the HTTP request path (the short URL key is the request path minus the first character, this can be written in Go as [1:]; for the request “/abc” the key would be “abc”), retrieves the corresponding long URL from the store with the Get function, and sends an HTTP redirect to the user. If the URL is not found, a 404 “Not Found” error is sent instead.

func Redirect(w http.ResponseWriter, r *http.Request) {
  key := r.URL.Path[1:]
  url := store.Get(key)
  if url == "" {
    http.NotFound(w, r)
    return
  }
  http.Redirect(w, r, url, http.StatusFound)
}

Here, http.NotFound and http.Redirect are helpers for sending common HTTP responses.

Now, we have discussed all the code of Version-1. Compile and run the program, which starts the web-server:

This code requires the following environment variables to execute:
GOROOT
/usr/local/go
GOPATH
//root/usr/local/go/src
PATH
//root/usr/local/go/src/bin:/usr/local/go/bin://root/usr/local/go/src/bin:/usr/local/go/bin://root/usr/local/go/src/bin:/usr/local/go/bin://root/usr/local/go/src/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
/
store.go
key.go
main.go

Note: If you’re running this code on your local machine, use port number 8080 instead of 3000 at line 21.

Testing the program#

Click the Run button, and wait for the terminal to start. Once it starts, type go run *.go and perform the following steps:

Created with Fabric.js 3.6.2 Click on this link
Step 1

1 of 3

Created with Fabric.js 3.6.2 Browser opens a page
Step 1

2 of 3

Created with Fabric.js 3.6.2 Add "/add" at the end of the URL
Step 2

3 of 3

This starts our Add handler function. There isn’t yet any url variable in the form, so the response is the HTML-form which asks for input:

Add Handler
Add Handler

Add a (long) URL for which you want a short equivalent, like http://golang.org/pkg/bufio/#Writer, and press the button. The application makes a short URL for you and prints a key, e.g., 0.

Response of Add Handler
Response of Add Handler

Copy and paste the same URL you did before in your browser address box and append the key at the end to it, received as a response by Add handler.

Redirect Handler
Redirect Handler
Response of Redirect Handler
Response of Redirect Handler

The result is the Redirect handler in action, and the page of the long URL is shown. You can stop this process with CTRL/C in the terminal window.

Note: If you are running this application on a local machine, to add a URL, open a browser and request the URL: http://localhost:8080/add. Then, add the long URL. Let’s suppose the server responds with 0 as a key. Lastly, request the URL: http://localhost:8080/0.


Our application is now successful in redirecting to the same page with the shorter URL. Our next concern is having proper storage for URLs that have already been made shorter. Let’s cover this in detail in the next lesson.

Setting up the Structure of Application

Persistent Storage with gob